/**
 * Copyright (c) 2019 Coder League
 * All rights reserved.
 *
 * File：AbstractMessage.java
 * History:
 *         2019年5月30日: Initially created, Chrise.
 */
package club.coderleague.ilsp.common.domain.beans.wxmp;

import java.io.InputStream;
import java.io.Serializable;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

/**
 * 消息基类。
 * @author Chrise
 */
@Getter
@Setter
@NoArgsConstructor
public abstract class AbstractMessage implements MessageType, Serializable {
	private static final long serialVersionUID = 4634235017309354324L;
	private static final Logger logger = LoggerFactory.getLogger(AbstractMessage.class);
	
	private static final String tagXml = "xml";
	private static final String tagToUserName = "ToUserName";
	private static final String tagFromUserName = "FromUserName";
	private static final String tagCreateTime = "CreateTime";
	private static final String tagMsgType = "MsgType";
	
	private String toUserName;
	private String fromUserName;
	private long createTime;
	private String msgType;
	
	/**
	 * 消息构造方法。
	 * @param toUserName 接收者。
	 * @param fromUserName 发送者。
	 * @param msgType 消息类型。
	 */
	public AbstractMessage(String toUserName, String fromUserName, String msgType) {
		this.toUserName = toUserName;
		this.fromUserName = fromUserName;
		this.createTime = System.currentTimeMillis();
		this.msgType = msgType;
	}
	
	/**
	 * 获取消息类型。
	 * @author Chrise 2019年5月30日
	 * @return 消息类型。
	 */
	public abstract String getMsgType();
	
	/**
	 * 序列化。
	 * @author Chrise 2019年5月30日
	 * @return 消息的XML形式字符串。
	 */
	public String serialize() {
		Element root = DocumentHelper.createElement(tagXml);
		addToXml(root);
		
		String xml = root.asXML();
		return xml;
	}
	
	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this, ToStringStyle.DEFAULT_STYLE);
	}
	
	/**
	 * 解析。
	 * @author Chrise 2019年5月30日
	 * @param in 输入流对象。
	 * @return 消息对象。
	 */
	public static AbstractMessage parse(InputStream in) {
		try {
			Document doc = new SAXReader().read(in);
			Element root = doc.getRootElement();
			return parse(root);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
		}
		return null;
	}
	
	/**
	 * 添加到XML对象。
	 * @author Chrise 2019年5月30日
	 * @param root XML根节点对象。
	 */
	protected void addToXml(Element root) {
		root.addElement(tagToUserName).addCDATA(this.toUserName);
		root.addElement(tagFromUserName).addCDATA(this.fromUserName);
		root.addElement(tagCreateTime).setText(String.valueOf(this.createTime));
		root.addElement(tagMsgType).addCDATA(this.msgType);
	}
	
	/**
	 * 解析。
	 * @author Chrise 2019年5月30日
	 * @param root XML根节点对象。
	 */
	protected void parseInternal(Element root) {
		this.toUserName = root.elementTextTrim(tagToUserName);
		this.fromUserName = root.elementTextTrim(tagFromUserName);
		this.createTime = Long.parseLong(root.elementTextTrim(tagCreateTime));
		this.msgType = root.elementTextTrim(tagMsgType);
	}
	
	/**
	 * 解析。
	 * @author Chrise 2019年5月30日
	 * @param root XML根节点对象。
	 * @return 消息对象。
	 */
	protected static AbstractMessage parse(Element root) {
		String msgType = root.elementTextTrim(tagMsgType);
		switch (msgType) {
			case EVENT:
				return AbstractEventMessage.parse(root);
			default:
				logger.warn("Unknown message [{}].", msgType);
				return null;
		}
	}
}
